package mylibrary.concurrent;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;

public class ConcurrentCalculator {

	private ExecutorService exec;
	private int cpuCoreNumber;
	private List<FutureTask> tasks = new ArrayList<FutureTask>();
	
	class SumCalculator implements Callable {
		
		private int[] numbers;
		private int start;
		private int end;
		
		public SumCalculator(final int[] numbers, int start, int end) {
			this.numbers = numbers;
			this.start = start;
			this.end = end;
		}
		
		public Long call() {
			Long sum = 0l;
			for(int i = start; i < end; i++) {
				sum += numbers[i];
			}
			return sum;
		}
	}
	
	public ConcurrentCalculator() {
		cpuCoreNumber = Runtime.getRuntime().availableProcessors();
		
		System.out.println("This computer has " + cpuCoreNumber + " processors");
		
		exec = Executors.newFixedThreadPool(cpuCoreNumber);
	}
	
	public Long sum(final int[] numbers) {
		
		for(int i = 0; i < cpuCoreNumber; i++) {
			
			int increment = numbers.length / cpuCoreNumber + 1;
			int start = increment * i;
			int end = increment * i + increment;
			if(end > numbers.length) {
				end = numbers.length;
			}
			SumCalculator subCal = new SumCalculator(numbers, start, end);
			FutureTask task = new FutureTask(subCal);
			tasks.add(task);
			
			if(!exec.isShutdown()) {
				exec.submit(task);
			}
		}
		return getResult();
	}
	
	public Long getResult() {
		Long result = 0l;
		for(FutureTask task:tasks) {
			try {
				Long subSum = (Long)task.get();
				result += subSum;
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
		return result;
	}
	public void close() {
		exec.shutdown();
	}
	
	public static void main(String[] args) {
		int[] numbers = new int[100000000];
		
		for(int i=0; i<100000000; i++) {
			numbers[i] = i;
		}

		System.out.println("Start time: " + System.currentTimeMillis());
		
		ConcurrentCalculator clo = new ConcurrentCalculator();
		Long result = clo.sum(numbers);
		
		System.out.println("End   time: " + System.currentTimeMillis());
		
		System.out.println(result);
		clo.close();
	}
	                                        
}
